package gui

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/storage"
	"fyne.io/fyne/v2/widget"

	"github.com/liyixin123/my-cos-project/internal/files"
)

const defaultWSLCommandTemplate = `$OutputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; & 'c:\Users\liyixin\Desktop\upload-pic\run-cos.ps1' upload '{}'`

// FileOperations 文件操作管理器
type FileOperations struct {
	app *App
}

// NewFileOperations 创建文件操作管理器
func NewFileOperations(app *App) *FileOperations {
	return &FileOperations{
		app: app,
	}
}

// ShowUploadDialog 显示上传对话框
func (fo *FileOperations) ShowUploadDialog() {
	if !fo.app.useCLIUpload && fo.app.client == nil {
		fo.app.showError("未连接", fmt.Errorf("请先配置并连接到存储服务"))
		return
	}

	dialog := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
		if err != nil {
			fo.app.showError("选择文件失败", err)
			return
		}
		if reader == nil {
			return
		}
		defer reader.Close()

		localPath := reader.URI().Path()

		// 根据配置决定使用哪种上传方式
		if fo.app.useCLIUpload {
			fo.UploadFileWithCLI(localPath)
		} else {
			fo.UploadFile(localPath)
		}
	}, fo.app.window)

	dialog.SetFilter(storage.NewExtensionFileFilter([]string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".ico",
		".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".rtf",
		".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a",
		".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm",
		".zip", ".rar", ".7z", ".tar", ".gz", ".bz2",
		".go", ".js", ".ts", ".py", ".java", ".cpp", ".c", ".h", ".css", ".html", ".xml", ".json", ".yaml", ".yml"}))
	dialog.Show()
}

// UploadFileWithCLI 使用CLI上传文件
func (fo *FileOperations) UploadFileWithCLI(localPath string) {
	fo.uploadFileWithCLI(localPath, filepath.Base(localPath), nil)
}

func (fo *FileOperations) uploadFileWithCLI(localPath, displayName string, cleanup func()) {
	cfg := fo.app.configManager.Config()
	if cfg == nil {
		err := fmt.Errorf("未加载配置")
		fo.app.setStatus("CLI上传失败: " + err.Error())
		fo.app.showError("上传失败", err)
		if cleanup != nil {
			go cleanup()
		}
		return
	}

	fo.app.setStatus("正在通过CLI上传: " + displayName)
	progressBar := fo.app.uiComponents.GetProgressBar()
	if progressBar != nil {
		progressBar.Show()
		progressBar.SetValue(0)
	}

	go func() {
		output, cmdErr := fo.runCLIUpload(cfg, localPath)

		fyne.Do(func() {
			if bar := fo.app.uiComponents.GetProgressBar(); bar != nil {
				bar.Hide()
			}

			if cmdErr != nil {
				fo.app.setStatus("CLI上传失败: " + cmdErr.Error())
				errorMsg := fmt.Sprintf("CLI上传失败: %v", cmdErr)
				if len(output) > 0 {
					errorMsg = fmt.Sprintf("%s\n输出:\n%s", errorMsg, string(output))
				}
				fo.app.showError("上传失败", fmt.Errorf(errorMsg))
			} else {
				fo.app.setStatus("CLI上传完成: " + displayName)
				infoMsg := fmt.Sprintf("文件上传成功: %s\n\nCLI输出:\n%s", displayName, string(output))
				fileURL, markdownLink := extractCLIUploadLinks(string(output), displayName)

				outputEntry := widget.NewMultiLineEntry()
				outputEntry.SetText(string(output))
				outputEntry.Wrapping = fyne.TextWrapWord
				outputEntry.Disable()

				label := widget.NewLabel(infoMsg)
				label.Wrapping = fyne.TextWrapWord

				copyLinkButton := widget.NewButton("复制访问链接", func() {
					if fileURL == "" {
						fo.app.window.Clipboard().SetContent(string(output))
						fo.app.showInfo("未解析到访问链接，已复制 CLI 输出")
						return
					}
					fo.app.window.Clipboard().SetContent(fileURL)
					fo.app.showInfo("已复制访问链接到剪贴板")
				})

				copyMarkdownButton := widget.NewButton("复制Markdown链接", func() {
					if markdownLink == "" {
						fo.app.window.Clipboard().SetContent(string(output))
						fo.app.showInfo("未解析到 Markdown 链接，已复制 CLI 输出")
						return
					}
					fo.app.window.Clipboard().SetContent(markdownLink)
					fo.app.showInfo("已复制 Markdown 链接到剪贴板")
				})

				content := container.NewVBox(
					label,
					widget.NewSeparator(),
					widget.NewLabel("CLI输出详情:"),
					outputEntry,
				)

				scrollContainer := container.NewScroll(content)
				scrollContainer.SetMinSize(fyne.NewSize(500, 300))

				buttonContainer := container.NewHBox(
					layout.NewSpacer(),
					copyLinkButton,
					copyMarkdownButton,
				)

				mainContainer := container.NewBorder(nil, buttonContainer, nil, nil, scrollContainer)
				dialog.NewCustom("上传成功", "关闭", mainContainer, fo.app.window).Show()

				fo.app.refreshFileList()
			}

			if cleanup != nil {
				go cleanup()
			}
		})
	}()
}

func extractCLIUploadLinks(output string, fallbackName string) (fileURL string, markdownLink string) {
	normalized := strings.ReplaceAll(output, "\r\n", "\n")
	lines := strings.Split(normalized, "\n")

	for idx := 0; idx < len(lines); idx++ {
		line := strings.TrimSpace(lines[idx])
		if line == "" {
			continue
		}

		switch {
		case strings.HasPrefix(line, "Markdown Link:"):
			markdownLink = strings.TrimSpace(strings.TrimPrefix(line, "Markdown Link:"))
		case strings.HasPrefix(line, "[Upload Success]:"):
			if idx+1 < len(lines) {
				next := strings.TrimSpace(lines[idx+1])
				if strings.HasPrefix(next, "http://") || strings.HasPrefix(next, "https://") {
					fileURL = next
				}
			}
		case strings.HasPrefix(line, "http://") || strings.HasPrefix(line, "https://"):
			if fileURL == "" {
				fileURL = line
			}
		}
	}

	if markdownLink == "" && fileURL != "" {
		nameWithoutExt := strings.TrimSuffix(fallbackName, filepath.Ext(fallbackName))
		if nameWithoutExt == "" {
			nameWithoutExt = fallbackName
		}
		markdownLink = fmt.Sprintf("![%s](%s)", nameWithoutExt, fileURL)
	}

	return fileURL, markdownLink
}

// UploadFile 上传文件
func (fo *FileOperations) UploadFile(localPath string) {
	displayName := filepath.Base(localPath)
	if fo.app.useCLIUpload {
		fo.uploadFileWithCLI(localPath, displayName, nil)
		return
	}

	remotePath := files.GenerateRemotePath(localPath, fo.app.currentDir)
	fo.startUpload(localPath, displayName, remotePath, nil)
}

// UploadTempFile 上传临时文件并在完成后执行清理
func (fo *FileOperations) UploadTempFile(localPath, originalName string, cleanup func()) {
	displayName := filepath.Base(originalName)
	if displayName == "" {
		displayName = filepath.Base(localPath)
	}

	if fo.app.useCLIUpload {
		fo.uploadFileWithCLI(localPath, displayName, cleanup)
		return
	}

	remotePath := files.GenerateRemotePath(localPath, fo.app.currentDir)
	fo.startUpload(localPath, displayName, remotePath, cleanup)
}

func (fo *FileOperations) startUpload(localPath, displayName, remotePath string, cleanup func()) {
	if fo.app.client == nil {
		if cleanup != nil {
			go cleanup()
		}
		return
	}

	fo.app.setStatus("正在上传: " + displayName)
	progressBar := fo.app.uiComponents.GetProgressBar()
	progressBar.Show()
	progressBar.SetValue(0)

	go func() {
		// 执行上传
		err := fo.app.client.UploadFile(remotePath, localPath)

		fyne.Do(func() {
			if bar := fo.app.uiComponents.GetProgressBar(); bar != nil {
				bar.Hide()
			}

			if err != nil {
				fo.app.setStatus("上传失败: " + err.Error())
				fo.app.showError("上传失败", err)
			} else {
				fo.app.setStatus("上传完成: " + displayName)
				fo.app.showInfo("文件上传成功: " + displayName)
				fo.app.refreshFileList()
			}

			if cleanup != nil {
				go cleanup()
			}
		})
	}()
}

// DownloadSelectedFile 下载选中的文件
func (fo *FileOperations) DownloadSelectedFile() {
	if fo.app.client == nil {
		fo.app.showError("未连接", fmt.Errorf("请先配置并连接到存储服务"))
		return
	}

	if fo.app.selectedFileIdx < 0 || fo.app.selectedFileIdx >= len(fo.app.files) {
		fo.app.showError("未选择文件", fmt.Errorf("请先选择要下载的文件"))
		return
	}

	selectedFile := fo.app.files[fo.app.selectedFileIdx]

	dialog := dialog.NewFileSave(func(writer fyne.URIWriteCloser, err error) {
		if err != nil {
			fo.app.showError("选择保存路径失败", err)
			return
		}
		if writer == nil {
			return
		}
		defer writer.Close()

		localPath := writer.URI().Path()

		fo.app.setStatus("正在下载文件...")

		go func() {
			err := fo.app.client.DownloadFile(selectedFile.Key, localPath)
			if err != nil {
				fyne.Do(func() {
					fo.app.setStatus("下载失败: " + err.Error())
					fo.app.showError("下载失败", err)
				})
				return
			}

			fyne.Do(func() {
				fo.app.setStatus("下载成功: " + filepath.Base(localPath))
				dialog.NewInformation("下载成功", "文件已保存到: "+localPath, fo.app.window).Show()
			})
		}()
	}, fo.app.window)

	dialog.SetFileName(selectedFile.Name)
	dialog.Show()
}

// DeleteSelectedFile 删除选中的文件
func (fo *FileOperations) DeleteSelectedFile() {
	if fo.app.client == nil {
		fo.app.showError("未连接", fmt.Errorf("请先配置并连接到存储服务"))
		return
	}

	if fo.app.selectedFileIdx < 0 || fo.app.selectedFileIdx >= len(fo.app.files) {
		fo.app.showError("未选择文件", fmt.Errorf("请先选择要删除的文件"))
		return
	}

	selectedFile := fo.app.files[fo.app.selectedFileIdx]

	dialog.NewConfirm("确认删除", "确定要删除文件 '"+selectedFile.Name+"' 吗？", func(confirmed bool) {
		if !confirmed {
			return
		}

		fo.app.setStatus("正在删除文件...")

		go func() {
			err := fo.app.client.DeleteFile(selectedFile.Key)
			if err != nil {
				fyne.Do(func() {
					fo.app.setStatus("删除失败: " + err.Error())
					fo.app.showError("删除失败", err)
				})
				return
			}

			fyne.Do(func() {
				fo.app.setStatus("删除成功: " + selectedFile.Name)
				fo.app.refreshFileList()
			})
		}()
	}, fo.app.window).Show()
}

// CopyLinkToClipboard 复制文件链接到剪贴板
func (fo *FileOperations) CopyLinkToClipboard() {
	if fo.app.selectedFileIdx < 0 || fo.app.selectedFileIdx >= len(fo.app.files) {
		fo.app.showError("未选择文件", fmt.Errorf("请先选择文件"))
		return
	}

	selectedFile := fo.app.files[fo.app.selectedFileIdx]
	url := fo.app.client.GetFileURL(selectedFile.Key)

	fo.app.window.Clipboard().SetContent(url)
	fo.app.showInfo("文件链接已复制到剪贴板")
}

// CopyMarkdownLinkToClipboard 复制Markdown格式链接到剪贴板
func (fo *FileOperations) CopyMarkdownLinkToClipboard() {
	if fo.app.selectedFileIdx < 0 || fo.app.selectedFileIdx >= len(fo.app.files) {
		fo.app.showError("未选择文件", fmt.Errorf("请先选择文件"))
		return
	}

	selectedFile := fo.app.files[fo.app.selectedFileIdx]
	url := fo.app.client.GetFileURL(selectedFile.Key)
	markdownLink := fmt.Sprintf("[%s](%s)", selectedFile.Name, url)

	fo.app.window.Clipboard().SetContent(markdownLink)
	fo.app.showInfo("Markdown格式链接已复制到剪贴板")
}

func (fo *FileOperations) runCLIUpload(cfg *Config, localPath string) ([]byte, error) {
	if cfg.UseCLIWSL {
		return fo.runCLIInWSL(cfg, localPath)
	}

	cmdPath := strings.TrimSpace(cfg.CLIPath)
	if cmdPath == "" {
		path, err := fo.app.resolveCLIBinaryPath()
		if err != nil {
			return nil, err
		}
		cmdPath = path
	} else {
		if absPath, err := filepath.Abs(cmdPath); err == nil {
			cmdPath = absPath
		}
		fo.app.cliBinaryPath = cmdPath
	}

	cmd := exec.Command(cmdPath, "upload", localPath)
	return cmd.CombinedOutput()
}

func (fo *FileOperations) runCLIInWSL(cfg *Config, localPath string) ([]byte, error) {
	if runtime.GOOS != "windows" {
		return nil, fmt.Errorf("WSL 模式仅在 Windows 可用")
	}

	template := strings.TrimSpace(cfg.CLIWSLCommand)
	if template == "" {
		template = defaultWSLCommandTemplate
	}

	if !strings.Contains(template, "{}") {
		return nil, fmt.Errorf("WSL 命令模板缺少 {} 占位符")
	}

	safePath := strings.ReplaceAll(localPath, "'", "''")
	command := strings.ReplaceAll(template, "{}", safePath)

	cmd := exec.Command("powershell.exe", "-NoProfile", "-Command", command)
	return cmd.CombinedOutput()
}
